﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Custom.Infrastructure.EventBus
{
    public class EventBus
    {
        /// <summary>
        /// 事件总线对象
        /// </summary>
        private static EventBus _eventBus = null;

        /// <summary>
        /// 领域模型事件句柄字典，用于存储领域模型的句柄
        /// </summary>
        private static Dictionary<Type, List<object>> _dicEventHandler = new Dictionary<Type, List<object>>();

        /// <summary>
        /// 附加领域模型处理句柄时，锁住
        /// </summary>
        private readonly object _syncObject = new object();

        public static EventBus Instance
        {
            get
            {
                return _eventBus ?? (_eventBus = new EventBus());
            }
        }


        #region 订阅事件

        public void UnSubscribe<TEvent>(IEventHandler<TEvent> eventHandler) where TEvent : ITEventEntity
        {
            lock(_syncObject)
            { 
                var eventType = typeof(TEvent);
                if (!_dicEventHandler.ContainsKey(eventType)) return;
                var handlers = _dicEventHandler[eventType];
                if (handlers == null) return;
                if (handlers.Contains(eventHandler))
                {
                    handlers.Remove(eventHandler);
                }
            }
        }

        public void Subscribe<TEvent>(IEventHandler<TEvent> eventHandler) where TEvent : ITEventEntity
        {
            //同步锁
            lock (_syncObject)
            {
                //获取领域模型的类型
                var eventType = typeof(TEvent);
                //如果此领域类型在事件总线中已注册过
                if (_dicEventHandler.ContainsKey(eventType))
                {
                    var handlers = _dicEventHandler[eventType];
                    if (handlers != null)
                    {
                        if (!handlers.Contains(eventHandler))
                        {
                            handlers.Add(eventHandler);
                        }
                    }
                    else
                    {
                        handlers = new List<object>
                        {
                            eventHandler
                        };
                    }
                }
                else
                {
                    _dicEventHandler.Add(eventType, new List<object> { eventHandler });
                }
            }
        }

        /// <summary>
        /// 订阅事件实体
        /// </summary>
        /// <param name="type"></param>
        /// <param name="subTypeList"></param>
        public void Subscribe<TEvent>(Action<TEvent> eventHandlerFunc)
            where TEvent : ITEventEntity
        {
            Subscribe<TEvent>(eventHandlerFunc);
        }
        public void Subscribe<TEvent>(IEnumerable<IEventHandler<TEvent>> eventHandlers)
            where TEvent : ITEventEntity
        {
            foreach (var eventHandler in eventHandlers)
            {
                Subscribe<TEvent>(eventHandler);
            }
        }

        #endregion


        #region 发布事件

        public void Publish<TEvent>(TEvent tEvent, Action<TEvent, bool, Exception> callback) where TEvent : ITEventEntity
        {
            lock (_syncObject)
            {
                var eventType = typeof(TEvent);
                if (_dicEventHandler.ContainsKey(eventType) && _dicEventHandler[eventType] != null &&
                    _dicEventHandler[eventType].Count > 0)
                {
                    var handlers = _dicEventHandler[eventType];
                    try
                    {
                        foreach (var handler in handlers)
                        {                            
                            var eventHandler = handler as IEventHandler<TEvent>;
                            eventHandler.HandleEvent(tEvent);
                            Console.WriteLine($"###################### publish --> {(tEvent as TEventEntity).ActionName}");
                            callback(tEvent, true, null);
                        }
                    }
                    catch (Exception ex)
                    {
                        callback(tEvent, false, ex);
                    }
                }
                else
                {
                    callback(tEvent, false, null);
                }
        }
    }

        #endregion
    }
}
